iT邦幫忙

2024 iThome 鐵人賽

DAY 26
0
Kubernetes

都什麼年代了,還在學 Kubernetes系列 第 26

學 Kubernetes 的第二十六天 - Scheduling - 指派 Pod 到節點

  • 分享至 

  • xImage
  •  

在上一章節中,我們瞭解了調度的基本邏輯,並初步認識了相關功能。今天,我們將深入探討如何透過各種方式控制 Pod 的調度,特別是如何將 Pod 指派到特定的節點上,以滿足不同的應用需求和資源條件。這將幫助我們更精確地管理資源,並確保應用程序的穩定性和效能。

概述

通常情況下,Kubernetes 調度器會自動對 Pod 進行合理的放置,但有時我們可能需要進一步控制 Pod 被部署到的節點。為此,我們可以使用以下幾種方法來約束 Pod 在特定的節點上運行,或優先在某些節點上運行。例如,確保 Pod 最終部署在配備 SSD 的節點上,或者將大量通訊的 Pod 放置在同一個可用區,以減少延遲並提高效能。

nodeSelector

nodeSelector 是最簡單的節點選擇約束。在 Pod 的規約中新增 nodeSelector 欄位,指定目標節點所需的節點標籤。Kubernetes 只會將 Pod 調度到符合這些標籤的節點上。

親和性與反親和性

nodeSelector 提供了一種基本的節點選擇方法,而親和性與反親和性 (Affinity and Anti-Affinity) 則擴展了這種功能,允許更靈活的約束。這些約束可以根據節點標籤或其他 Pod 的標籤來決定 Pod 的調度。

節點親和性

節點親和性 (Node Affinity) 概念上類似於 nodeSelector 的加強版,一樣是基於 Node Label 來選擇節點但是提供更細部的操作。除了兩種不同的選擇策略外,對於 Label 的決策方式也更加彈性,不是單純的 "Equal" 去比較而已。

目前其提供兩種選擇策略:

  • requiredDuringSchedulingIgnoredDuringExecution:概念與原先的 NodeSelector 類似,是個硬性條件,若沒有任何節點符合條件則 Pod 就會處於 Pending 狀態。
  • preferredDuringSchedulingIgnoredDuringExecution:相較於彈性,就是盡量滿足即可,若實在沒有滿足則還是可以調度到這個 Node 上。

Pod 間親和性與反親和性

Pod 間親和性與反親和性基於已經在節點上運行的 Pod 的標籤來約束新 Pod 的調度。例如,如果某些 Pod 具有相同的應用標籤(如 app=frontend),我們可以使用親和性來確保這些 Pod 儘量調度到相同的節點上,以減少網路延遲。而反親和性則可以用於確保同一應用的 Pod 分佈在不同節點上,以提高高可用性,防止單點故障。這可以根據拓撲域(如區域、可用區、節點等)來控制 Pod 的分佈。

注意

  • Pod 間親和性和反親和性都需要相當的計算量,因此會在大規模叢集中顯著降低調度速度。 不建議在包含數百個節點的叢集中使用這類設定。
  • Pod 反親和性需要節點上存在一致性的標籤。換言之,叢集中每個節點都必須擁有與 topologyKey 匹配的標籤。 如果某些或者所有節點上不存在所指定的 topologyKey 標籤,調度行為可能與預期的不同。

修改標籤的影響

由於親和性與反親和性的操作都跟 Label 有關係,因此勢必會有人好奇若動態調整 Label 會影響當前運行的 Pod 嗎?IgnoredDuringExecution 這個含意就是字面上的意思,這些決策都會忽略執行期間的 Label 變動,一旦 Pod 被選定指派後,Scheduler 就不會去干涉了。

nodeName

nodeName 是更為直接的節點選擇方式,優先度比 nodeSelector親和性與反親和性 更高。這種方式特別適合在需要將 Pod 部署到特定節點的場景中使用,例如在單一節點進行測試或應對特殊需求的情況下。使用 nodeName 可以確保 Pod 被精確地分配到指定的節點,從而方便測試和排除故障。當在 Pod 規約中指定 nodeName 欄位時,調度器會忽略該 Pod,而直接將其分配給指定的節點。需要注意的是,如果指定的節點不存在或無法提供所需資源,Pod 可能無法運行。

Pod 拓撲分佈約束

在 Kubernetes 中,拓撲分佈約束 (Topology Spread Constraints) 是用來控制 Pod 在拓撲域(例如區域、可用區、節點等)之間的分佈,以實現更高的可用性和容錯能力。

以下是 topologySpreadConstraints 的主要欄位:

  • maxSkew:定義在各個拓撲域之間允許的最大 Pod 數量差異。
  • topologyKey:指定拓撲域的標籤鍵,例如 topology.kubernetes.io/zone
  • whenUnsatisfiable:指定在無法滿足分佈約束時的處理方式,可以是 DoNotScheduleScheduleAnyway
  • labelSelector:用來選擇需要應用拓撲分佈約束的 Pod 標籤。

實作

準備工作

驗證 scheduler 的行為

當我們要部署多個 pod ,在我們不使用額外設定干涉 kube-scheduler 情況下,pod 會被均勻分佈在工作節點裡。下面我們來驗證一下。

  • 查看叢集的 node 列表
kubectl get node
---
NAME                    STATUS   ROLES           AGE   VERSION
wslkind-control-plane   Ready    control-plane   8d    v1.30.0
wslkind-worker          Ready    <none>          8d    v1.30.0
wslkind-worker2         Ready    <none>          8d    v1.30.0

可以看到叢集有一個主節點,兩個工作節點。

組態檔案: deploy.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  creationTimestamp: null
  labels:
    app: test-deploy
  name: test-deploy
spec:
  replicas: 6
  selector:
    matchLabels:
      app: test-pod
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: test-pod
    spec: 
      containers:
      - image: nginx
        name: nginx

這個組態檔案描述了一個最基本的 deployment。他會部署 6 個 pod。

  • 建立 Deployment
kubectl apply -f deploy.yaml
  • 驗證 Pod 的部署狀況
kubectl get pod -l app=test-pod -o wide

結果如下

NAME                           READY   STATUS    RESTARTS   AGE   IP            NODE              NOMINATED NODE   READINESS GATES
test-deploy-7f7cd9b788-2f4fj   1/1     Running   0          60s   10.244.2.26   wslkind-worker2   <none>           <none>
test-deploy-7f7cd9b788-d6xvc   1/1     Running   0          60s   10.244.2.27   wslkind-worker2   <none>           <none>
test-deploy-7f7cd9b788-jwsxr   1/1     Running   0          60s   10.244.2.25   wslkind-worker2   <none>           <none>
test-deploy-7f7cd9b788-mfwg8   1/1     Running   0          60s   10.244.1.4    wslkind-worker    <none>           <none>
test-deploy-7f7cd9b788-p228s   1/1     Running   0          60s   10.244.1.5    wslkind-worker    <none>           <none>
test-deploy-7f7cd9b788-sqdm4   1/1     Running   0          4s    10.244.1.6    wslkind-worker    <none>           <none>

可以看到,Pod 確實被平均分配給兩個工作節點。

  • 清理資源
kubectl delete -f deploy.yaml

給節點加自定義標籤

本章節的實作中,我們會需要透過標籤來選擇 wslkind-worker2 節點,因此需要事先打上標籤。

  • 列出節點資訊,包含標籤
kubectl get node wslkind-worker2 --show-labels

結果如下

NAME              STATUS   ROLES    AGE   VERSION   LABELS
wslkind-worker2   Ready    <none>   8d    v1.30.0   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=wslkind-worker2,kubernetes.io/os=linux
  • 添加自定義標籤
kubectl label nodes wslkind-worker2 disktype=ssd
  • 驗證節點標籤資訊
kubectl get node wslkind-worker2 --show-labels

結果如下

NAME              STATUS   ROLES    AGE   VERSION   LABELS
wslkind-worker2   Ready    <none>   8d    v1.30.0   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,disktype=ssd,kubernetes.io/arch=amd64,kubernetes.io/hostname=wslkind-worker2,kubernetes.io/os=linux

節點確實帶有 disktype=ssd

也可以使用 --label-columns (-L) 參數列出指定標籤:

kubectl get node wslkind-worker2 -L disktype

結果如下

NAME              STATUS   ROLES    AGE   VERSION   DISKTYPE
wslkind-worker2   Ready    <none>   8d    v1.30.0   ssd

可以看到 wslkind-worker2 節點已有 disktype=ssd 標籤。

透過 nodeSelector 指派

組態檔案: deploy.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  creationTimestamp: null
  labels:
    app: test-deploy
  name: test-deploy
spec:
  replicas: 5
  selector:
    matchLabels:
      app: test-pod
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: test-pod
    spec:
      containers:
      - image: nginx
        name: nginx
      nodeSelector:
        disktype: ssd

此組態檔案描述了一個擁有節點選擇器 disktype: ssd 的 Pod。這表明該 deployment 的所有 Pod 將被調度到有 disktype=ssd 標籤的節點。

  • 建立 Deployment
kubectl apply -f deploy.yaml
  • 驗證 Pod 確實運行在 wslkind-worker2 節點上
kubectl get pod -l app=test-pod -o wide

結果如下

NAME                          READY   STATUS    RESTARTS   AGE    IP           NODE              NOMINATED NODE   READINESS GATES
test-deploy-f465f68c4-dbs4z   1/1     Running   0          114s   10.244.2.6   wslkind-worker2   <none>           <none>
test-deploy-f465f68c4-fsjkk   1/1     Running   0          114s   10.244.2.9   wslkind-worker2   <none>           <none>
test-deploy-f465f68c4-vk28b   1/1     Running   0          114s   10.244.2.8   wslkind-worker2   <none>           <none>
test-deploy-f465f68c4-wbzq8   1/1     Running   0          114s   10.244.2.7   wslkind-worker2   <none>           <none>
test-deploy-f465f68c4-xgh74   1/1     Running   0          114s   10.244.2.5   wslkind-worker2   <none>           <none>

透過 nodeName 指派

組態檔案: deploy.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  creationTimestamp: null
  labels:
    app: test-deploy
  name: test-deploy
spec:
  replicas: 5
  selector:
    matchLabels:
      app: test-pod
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: test-pod
    spec:
      nodeName: fwslkind-worker2 # 調度 Pod 到特定的節點
      containers:
      - image: nginx
        name: nginx
  • 建立 Deployment
kubectl apply -f deploy.yaml
  • 驗證 Pod 確實運行在 wslkind-worker2 節點上
kubectl get pod -l app=test-pod -o wide

結果如下

NAME                           READY   STATUS    RESTARTS   AGE   IP            NODE              NOMINATED NODE   READINESS GATES
test-deploy-6d4c4bcf7f-4z92c   1/1     Running   0          10s   10.244.2.18   wslkind-worker2   <none>           <none>
test-deploy-6d4c4bcf7f-fhc5d   1/1     Running   0          10s   10.244.2.17   wslkind-worker2   <none>           <none>
test-deploy-6d4c4bcf7f-k4vz6   1/1     Running   0          10s   10.244.2.19   wslkind-worker2   <none>           <none>
test-deploy-6d4c4bcf7f-rbdz9   1/1     Running   0          10s   10.244.2.16   wslkind-worker2   <none>           <none>
test-deploy-6d4c4bcf7f-sxtht   1/1     Running   0          10s   10.244.2.15   wslkind-worker2   <none>           <none>

透過節點親和性指派

組態檔案: deploy.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  creationTimestamp: null
  labels:
    app: test-deploy
  name: test-deploy
spec:
  replicas: 5
  selector:
    matchLabels:
      app: test-pod
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: test-pod
    spec:
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: disktype
                operator: In
                values:
                - ssd     
      containers:
      - image: nginx
        name: nginx

此組態檔案描述了一個 deployment,內含的 Pod 有一個節點親和性組態 requiredDuringSchedulingIgnoredDuringExecutiondisktype=ssd。 這意味著 pod 只會被調度到具有 disktype=ssd 標籤的節點上。

  • 建立 deployment
kubectl apply -f deploy.yaml
  • 驗證 Pod 確實運行在 wslkind-worker2 節點上
kubectl get pod -l app=test-pod -o wide

結果如下

NAME                           READY   STATUS              RESTARTS   AGE   IP            NODE              NOMINATED NODE   READINESS GATES
test-deploy-78768c794b-gz4rc   1/1     Running             0          8s    10.244.2.23   wslkind-worker2   <none>           <none>
test-deploy-78768c794b-hpxpk   0/1     ContainerCreating   0          8s    <none>        wslkind-worker2   <none>           <none>
test-deploy-78768c794b-jt8rd   1/1     Running             0          8s    10.244.2.20   wslkind-worker2   <none>           <none>
test-deploy-78768c794b-pkh4r   1/1     Running             0          8s    10.244.2.21   wslkind-worker2   <none>           <none>
test-deploy-78768c794b-qxzrs   1/1     Running             0          8s    10.244.2.22   wslkind-worker2   <none>           <none>

透過 Pod 拓撲分佈約束限制 Pod 分配

為了更好的呈現拓撲分佈的特性,我們建立新的叢集,來模仿實際生產環境。

建立新的叢集

示意圖如下

https://ithelp.ithome.com.tw/upload/images/20241010/20168212UTJDaln0xO.png

設定檔: multi-region-cluster.yaml

kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
  labels:
    topology.kubernetes.io/region: us-east-1
    topology.kubernetes.io/zone: us-east-1a
- role: worker
  labels:
    topology.kubernetes.io/region: us-east-1
    topology.kubernetes.io/zone: us-east-1c
- role: worker
  labels:
    topology.kubernetes.io/region: us-east-2
    topology.kubernetes.io/zone: us-east-2a
- role: worker
  labels:
    topology.kubernetes.io/region: us-east-2
    topology.kubernetes.io/zone: us-east-2c
- role: worker
  labels:
    topology.kubernetes.io/region: ap-northeast-1
    topology.kubernetes.io/zone: ap-northeast-1a
- role: worker

這是一個 KinD 設定檔,裡面包含了 5 個工作節點,透過標籤定義,分配給 3 個 Region。另外有 1 個工作節點,並沒有定義 Region 和 Zone。

  • 使用 KinD 建立叢集
kind create cluster --name multi-region-cluster --config multi-region-cluster.yaml

當我們使用 KinD 建立新的叢集,KinD 會自動幫我們產生合併對應的 context 內容,並且切換到剛建立的 context。

  • 查詢叢集列表
kind get clusters
---
multi-region-cluster
wslkind
  • 檢查節點的標籤
kubectl get no -L topology.kubernetes.io/region,topology.kubernetes.io/zone

輸出類似如下:

NAME                                 STATUS   ROLES           AGE   VERSION   REGION           ZONE
multi-region-cluster-control-plane   Ready    control-plane   42s   v1.30.0
multi-region-cluster-worker          Ready    <none>          20s   v1.30.0   us-east-1        us-east-1a
multi-region-cluster-worker2         Ready    <none>          21s   v1.30.0   us-east-1        us-east-1c
multi-region-cluster-worker3         Ready    <none>          20s   v1.30.0   us-east-2        us-east-2a
multi-region-cluster-worker4         Ready    <none>          20s   v1.30.0   us-east-2        us-east-2c
multi-region-cluster-worker5         Ready    <none>          21s   v1.30.0   ap-northeast-1   ap-northeast-1a
multi-region-cluster-worker6         Ready    <none>          20s   v1.30.0

實作情境題 1

來設想一下,我們的叢集在全球擁有多個節點,分佈在多個 Region 和 Zone。現在我們要部署最新的 Web 應用,打算平均的部署在 Region 上。

組態檔案: deploy.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  creationTimestamp: null
  labels:
    app: test-deploy
  name: test-deploy
spec:
  replicas: 20
  selector:
    matchLabels:
      app: test-pod
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: test-pod
    spec: 
      topologySpreadConstraints:
      - maxSkew: 1
        topologyKey: "topology.kubernetes.io/region"
        whenUnsatisfiable: DoNotSchedule
        labelSelector:
          matchLabels:
            app: test-pod
      containers:
      - image: nginx
        name: nginx

上述組態檔案描述了一個 Deployment,透過 topologySpreadConstraints 欄位,我們設定:

  • topology.kubernetes.io/region 這個標籤為基礎

  • 每個標籤之間,pod 數量相差不能超過 1 個

  • 沒有標籤的節點不調度

  • 建立 deployment

kubectl apply -f deploy.yaml
  • 驗證 Pod 拓撲的情況
kubectl get pod -l app=test-pod -o wide --sort-by=.spec.nodeName

--sort-by=.spec.nodeName 參數的意思是依照 nodeName 欄位排序,方便查看。

結果如下

NAME                           READY   STATUS    RESTARTS   AGE    IP           NODE                           NOMINATED NODE   READINESS GATES
test-deploy-567755cd66-57ld7   1/1     Running   0          115s   10.244.4.3   multi-region-cluster-worker    <none>           <none>
test-deploy-567755cd66-vs75b   1/1     Running   0          115s   10.244.4.2   multi-region-cluster-worker    <none>           <none>
test-deploy-567755cd66-5gffm   1/1     Running   0          115s   10.244.1.3   multi-region-cluster-worker2   <none>           <none>
test-deploy-567755cd66-wkzfq   1/1     Running   0          115s   10.244.1.2   multi-region-cluster-worker2   <none>           <none>
test-deploy-567755cd66-5rmnm   1/1     Running   0          115s   10.244.5.3   multi-region-cluster-worker3   <none>           <none>
test-deploy-567755cd66-r5qtc   1/1     Running   0          115s   10.244.5.2   multi-region-cluster-worker3   <none>           <none>
test-deploy-567755cd66-dmmvw   1/1     Running   0          115s   10.244.3.2   multi-region-cluster-worker4   <none>           <none>
test-deploy-567755cd66-z8mt2   1/1     Running   0          115s   10.244.3.3   multi-region-cluster-worker4   <none>           <none>
test-deploy-567755cd66-25ndq   1/1     Running   0          115s   10.244.2.2   multi-region-cluster-worker5   <none>           <none>
test-deploy-567755cd66-5dw7m   1/1     Running   0          115s   10.244.2.5   multi-region-cluster-worker5   <none>           <none>
test-deploy-567755cd66-6ml56   1/1     Running   0          115s   10.244.2.4   multi-region-cluster-worker5   <none>           <none>
test-deploy-567755cd66-9dfdb   1/1     Running   0          115s   10.244.2.3   multi-region-cluster-worker5   <none>           <none>

可以看到, Pod 很平均的被分佈到 3 個 Region 的節點之間,並且由於 multi-region-cluster-worker6 節點沒有標籤,並沒有被選中調度。

實作情境題 2

我們來組合之前提到的 NodeAffinity,達成更靈活的應用。

實作情境題 1,現在由於 us-east-2c 這個 Zone 的節點損壞,我們需要排除這個 Zone。

組態檔案: deploy.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  creationTimestamp: null
  labels:
    app: test-deploy
  name: test-deploy
spec:
  replicas: 20
  selector:
    matchLabels:
      app: test-pod
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: test-pod
    spec: 
      topologySpreadConstraints:
      - maxSkew: 1
        topologyKey: "topology.kubernetes.io/region"
        whenUnsatisfiable: DoNotSchedule
        labelSelector:
          matchLabels:
            app: test-pod
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: "topology.kubernetes.io/zone"
                operator: NotIn
                values:
                - us-east-2c
      containers:
      - image: nginx
        name: nginx
  • 建立 deployment
kubectl apply -f deploy.yaml
  • 驗證 Pod 拓撲的情況
kubectl get pod -l app=test-pod -o wide --sort-by=.spec.nodeName

結果如下

NAME                           READY   STATUS    RESTARTS   AGE    IP           NODE                           NOMINATED NODE   READINESS GATES
test-deploy-84c6b8c46b-ngczt   1/1     Running   0          26s   10.244.4.6    multi-region-cluster-worker    <none>           <none>
test-deploy-84c6b8c46b-gzws7   1/1     Running   0          26s   10.244.4.7    multi-region-cluster-worker    <none>           <none>
test-deploy-84c6b8c46b-phlnx   1/1     Running   0          29s   10.244.4.5    multi-region-cluster-worker    <none>           <none>
test-deploy-84c6b8c46b-mw6hv   1/1     Running   0          31s   10.244.1.6    multi-region-cluster-worker2   <none>           <none>
test-deploy-84c6b8c46b-9tvj2   1/1     Running   0          25s   10.244.1.7    multi-region-cluster-worker2   <none>           <none>
test-deploy-84c6b8c46b-ff5t5   1/1     Running   0          32s   10.244.1.5    multi-region-cluster-worker2   <none>           <none>
test-deploy-84c6b8c46b-6smxr   1/1     Running   0          32s   10.244.5.6    multi-region-cluster-worker3   <none>           <none>
test-deploy-84c6b8c46b-75sgl   1/1     Running   0          32s   10.244.5.7    multi-region-cluster-worker3   <none>           <none>
test-deploy-84c6b8c46b-78hcg   1/1     Running   0          26s   10.244.5.10   multi-region-cluster-worker3   <none>           <none>
test-deploy-84c6b8c46b-dhb68   1/1     Running   0          32s   10.244.5.8    multi-region-cluster-worker3   <none>           <none>
test-deploy-84c6b8c46b-xwqkc   1/1     Running   0          32s   10.244.5.5    multi-region-cluster-worker3   <none>           <none>
test-deploy-84c6b8c46b-clkgv   1/1     Running   0          27s   10.244.5.9    multi-region-cluster-worker3   <none>           <none>
test-deploy-84c6b8c46b-l58st   1/1     Running   0          32s   10.244.5.4    multi-region-cluster-worker3   <none>           <none>
test-deploy-84c6b8c46b-tjhmq   1/1     Running   0          32s   10.244.2.7    multi-region-cluster-worker5   <none>           <none>
test-deploy-84c6b8c46b-mlrhp   1/1     Running   0          28s   10.244.2.10   multi-region-cluster-worker5   <none>           <none>
test-deploy-84c6b8c46b-5whtr   1/1     Running   0          27s   10.244.2.11   multi-region-cluster-worker5   <none>           <none>
test-deploy-84c6b8c46b-jffbj   1/1     Running   0          31s   10.244.2.8    multi-region-cluster-worker5   <none>           <none>
test-deploy-84c6b8c46b-6dskd   1/1     Running   0          32s   10.244.2.6    multi-region-cluster-worker5   <none>           <none>
test-deploy-84c6b8c46b-tshgm   1/1     Running   0          29s   10.244.2.9    multi-region-cluster-worker5   <none>           <none>
test-deploy-84c6b8c46b-79tkt   1/1     Running   0          26s   10.244.2.12   multi-region-cluster-worker5   <none>           <none>

可以看到,這次 multi-region-cluster-worker4 這個處於 us-east-2c Zone 的節點被排除了,並且所有的 Pod 被平均分佈在剩餘合適的節點當中。

切換 context

記得切換回原來的 context,方便接下來的實作:

kubectl config use-context kind-wslkind

參考


上一篇
學 Kubernetes 的第二十五天 - Scheduling - 概述
下一篇
學 Kubernetes 的第二十七天 - Scheduling - Taints 和 Tolerations
系列文
都什麼年代了,還在學 Kubernetes37
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言